#include "prisoner.h"
#include "genome.h"
#include <stdlib.h>


extern int MATRIXWIDTH;
extern int bestchoice;

float ignoreRate = 0.1;
float redoRate = 0.7;
float addRate = 0.02;
float delRate = 0.08;
float mutRate = 0.95;

using namespace std;




Genome::Genome()
{
	score = 0;
	age = 0;
	totalScore = 0.0;
	/** make one rule **/
	rules.push_back(Decision());
	
}

void Genome::crossover(Genome& g1, Genome& g2, Genome& offspring)
{
	// clear offspring
	offspring.rules.clear();
	int g2size = g2.rules.size();

	// first part from g1
	// second part from g2
	int cp1 = randomInt(0,g1.rules.size()-1);
	int cp2 = randomInt(0,g2.rules.size()-1);


	if(cp1 == 0 && cp2 == g2.rules.size()-1) // if nothing was selected
	{
		for(list<Decision>::iterator iter = g1.rules.begin(); iter != g1.rules.end(); iter ++)
		{
			offspring.rules.push_back(*iter);
		}
		return;
	}

	/** cut 0~cp1 from g1 **/
	list<Decision>::iterator g1i = g1.rules.begin();
	for(int i = 0; i <= cp1; i++)
	{
		offspring.rules.push_back(*g1i);
		g1i++;
	}

	/** cut cp2~N from g2 **/
	list<Decision>::iterator g2i = g2.rules.begin();
	for(int i = cp2; i < g2size; i++)
	{
		if(offspring.rules.size()>=3999)
			break;
		offspring.rules.push_back(*g2i);
		g2i++;
	}
	

}


void Genome::mutate(Genome& g)
{
	g.mutate();
}

void _setTurn(int &f, int &t)
{
	if(flipCoin(ignoreRate))
		f = t = -1;
	else {
		f = randomInt(1,100);
		t = randomInt(f, 100);
	}
}

void _setChoice(int &p1, int &p2)
{
	if(flipCoin(ignoreRate))
		p1 = p2 = -1;
	else {
		p1 = randomInt(1,MATRIXWIDTH);
		p2 = randomInt(1,MATRIXWIDTH);
	
	}
}

void _setScore(int &f, int &t)
{
	if(flipCoin(ignoreRate))
		f = t = -1;
	else {
		f = randomInt(1,30);
		t = randomInt(f,30);
	}

}


void Genome::mutate()
{

	if(flipCoin(addRate) && rules.size()<3998)
	{
		rules.push_back(Decision());
	}

	if(flipCoin(delRate) && rules.size()>1)
	{
		int k = randomInt(0,rules.size()-1);
		int l = 0;
		for(list<Decision>::iterator i = rules.begin(); i != rules.end(); i++)
		{
			if(k == l) {
				rules.erase(i);
				break;
			}
			l++;
		}
	}

	
	for(list<Decision>::iterator i = rules.begin(); i != rules.end(); i++)
	{	
		Decision &d = *i;		
		
		if(flipCoin(mutRate))
		do {
			int choice = randomInt(0,19);

			switch(choice) {
				case 0:
					//0~1turn(1~100)
					_setTurn(d.raw[0], d.raw[1]);
					break;
				case 1:
					//2~7 (my,his...
					_setChoice(d.raw[2], d.raw[3]);
					break;
				case 2:
					_setScore(d.raw[4], d.raw[5]);
					break;
				case 3:
					_setScore(d.raw[6], d.raw[7]);
					break;
				case 4:
					//8~13
					_setChoice(d.raw[8], d.raw[9]);
					break;
				case 5:
					_setScore(d.raw[10], d.raw[11]);
					break;
				case 6:
					_setScore(d.raw[12], d.raw[13]);
					break;
				case 7:
					//14~21
					_setChoice(d.raw[14], d.raw[15]);
					break;
				case 8:
					_setScore(d.raw[16], d.raw[17]);					
					break;
				case 9:
					_setScore(d.raw[18], d.raw[19]);
					break;
				case 10:
					//20~25
					_setChoice(d.raw[20], d.raw[21]);
					break;
				case 11:
					_setScore(d.raw[22], d.raw[23]);
					break;
				case 12:
					_setScore(d.raw[24], d.raw[25]);
					break;
				case 13:
					//26~29	
					_setScore(d.raw[26], d.raw[27]);
					break;
				case 14:
					_setScore(d.raw[28], d.raw[29]);
					break;
				default:
					//30 
					if(flipCoin(ignoreRate))
						d.raw[30] = 1;
					else {
						d.raw[30] = randomInt(1,MATRIXWIDTH);
					}

			}
		
		} while(flipCoin(redoRate));

	}

}



void Genome::print(FILE* fp)
{
	fprintf(stdout, "%f\n", score);
	for(list<Decision>::iterator iter = rules.begin(); iter != rules.end(); iter++)
	{
		for(int i = 0; i < 31; i++)
		{
			fprintf(fp, "%d ", iter->raw[i]);
		}
		fprintf(fp, "\n");
	}
	// last line
	for(int i = 0; i < 30; i++)
		fprintf(fp, "-1 ");
	fprintf(fp, "%d\n",bestchoice);

	fprintf(stdout,"==\n");
}


bool flipCoin(float p)
{
		double r = ((double)rand() / ((double)(RAND_MAX)+(double)(1)));
		return (r<p) ? true : false;
}


int randomInt(int min,int max)
{
	int r = ((int)(rand() / (((double)RAND_MAX + 1)/ ((max))))+min);

	if( r < min || r > max)
		return max;
	return r;
	//return rand()%(max-min+1)+min;

}

